package com.milling.web.utils;

import com.milling.business.GlobalVariable;
import com.milling.business.service.SuspendTaskManager;
import com.milling.common.core.redis.RedisCache;
import com.milling.common.utils.SocketUtils;
import com.milling.common.utils.StringUtils;
import com.milling.common.utils.modbus.ModbusMasterPoolFactory;
import com.milling.common.utils.modbus.ModbusServiceFactory;
import com.milling.common.utils.modbus.ModbusTcpService;
import com.milling.common.utils.modbus.ModbusTcpServiceImpl;
import com.milling.common.utils.scanner.SocketListener;
import com.milling.framework.websocket.AlertConfirmWebSocketHandler;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import org.apache.commons.pool2.ObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Configuration
@ConditionalOnProperty(
        name = "mac.type",
        havingValue = "1",
        matchIfMissing = false // 当配置缺失时不加载
)
@Component
public class ConfigScanner implements SocketListener.MessageCallback {
    private static final Logger logger = LoggerFactory.getLogger(ConfigScanner.class);

    @Value("${config.ip}")
    private String configIp;

    @Value("${config.port}")
    private int configPort;

    /*@Value("${modbus.tcp.host}")
    private String host; // 从配置文件或命令行参数中读取

    @Value("${modbus.tcp.port}")
    private int port; // 从配置文件或命令行参数中读取

    @Value("${modbus.tcp.poolSize}")
    private int poolSize; // 从配置文件或命令行参数中读取

    @Value("${modbus.tcp.slaveId}")
    private int slaveId; // 从配置文件或命令行参数中读取
*/
    /*private final String host = "192.168.237.88"; // 从配置文件或命令行参数中读取

    private final int port = 502; // 从配置文件或命令行参数中读取

    private final int poolSize = 100; // 从配置文件或命令行参数中读取

    private int slaveId = 1;*/

    @Value("${modbus.tcp.host:192.168.237.88}") // 默认值防止未配置
    private String host;

    @Value("${modbus.tcp.port:502}")
    private int port;

    @Value("${modbus.tcp.poolSize:5}")
    private int poolSize;

    @Value("${modbus.tcp.slaveId:1}")
    private int slaveId;

    @Autowired
    private SuspendTaskManager suspendTaskManager;

    // 获取Modbus服务实例
//    private final ModbusTcpService modbusTcpService = ModbusServiceFactory.getModbusService(host, port);
    private ModbusTcpService modbusTcpService;

    //    @Autowired
//    private ModbusUtil modbusUtil;
    private SocketUtils socketUtils; // 声明为成员变量

    @Autowired
    private AlertConfirmWebSocketHandler webSocketHandler;

    @Autowired
    private RedisCache redisCache;

    // 添加定时任务执行器
    private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private volatile boolean isMonitoring = false;

    // 添加扫码器连接管理
    private volatile boolean reconnectFlag = true;
    private final int MAX_RETRIES = 5;
    private final int RETRY_INTERVAL = 3000;
    private SocketListener currentListener;
    private Thread listenerThread;

    @Autowired
    public ConfigScanner(@Value("${config.ip}") String configIp, @Value("${config.port}") int configPort) {
        this.configIp = configIp;
        this.configPort = configPort;
        this.socketUtils = new SocketUtils(configIp, configPort); // 初始化
    }

    @PostConstruct
    public void init() {
        // 验证配置参数
        if (configIp == null || configIp.trim().isEmpty()) {
            logger.error("ConfigScanner 配置错误: config.ip 未设置");
            return;
        }
        if (configPort <= 0 || configPort > 65535) {
            logger.error("ConfigScanner 配置错误: config.port 无效: {}", configPort);
            return;
        }

        // 初始化 Modbus 服务
        modbusTcpService = ModbusServiceFactory.getModbusService(host, port, poolSize);
        logger.info("ConfigScanner Modbus服务初始化完成 - {}:{}", host, port);
        logger.info("ConfigScanner 扫码器配置 - {}:{}", configIp, configPort);

        // 初始化扫码器连接 - 修复：需要建立持久连接来接收消息
        initConnection();

        // 启动PLC监控任务
        startPlcMonitoring();
    }

    /**
     * 启动PLC监控任务，定时检查PLC地址507的状态
     */
    private void startPlcMonitoring() {
        isMonitoring = true;
        scheduler.scheduleWithFixedDelay(() -> {
            if (!isMonitoring) return;

            try {
                int plc507Value = modbusTcpService.readHoldingRegister(slaveId, 507);
                if (plc507Value > 0) {
                    // PLC请求扫码，触发扫码器
                    logger.info("PLC请求扫码，地址507值: {}", plc507Value);
                    triggerScanning();
                }
            } catch (Exception e) {
                logger.error("监控PLC地址507失败", e);
            }
        }, 1, 1, TimeUnit.SECONDS);  // 每秒检查一次

        logger.info("ConfigScanner PLC监控任务已启动");
    }

    /**
     * 触发扫码器开始扫码 - 修复：通过已建立的连接发送命令
     */
    private void triggerScanning() {
        if (currentListener != null) {
            try (SocketUtils socketUtils = new SocketUtils(configIp, configPort)) {
                socketUtils.sendMessage("start");
                logger.info("触发配针扫码器开始扫码");
            } catch (Exception e) {
                logger.error("触发扫码器失败", e);
                // 如果发送失败，尝试重新连接
                if (reconnectFlag) {
                    initConnection();
                }
            }
        } else {
            logger.warn("扫码器连接未建立，尝试重新连接");
            initConnection();
        }
    }

    /**
     * 初始化扫码器连接
     */
    private synchronized void initConnection() {
        int retryCount = 0;
        while (reconnectFlag && retryCount < MAX_RETRIES) {
            try {
                closeExistingConnection(); // 清理旧连接

                java.net.Socket socket = new java.net.Socket(configIp, configPort);
                socket.setKeepAlive(true); // 启用Keep-Alive
                socket.setSoTimeout(30000); // 设置读超时

                currentListener = new SocketListener(socket, this);
                listenerThread = new Thread(currentListener);
                listenerThread.start();

                logger.info("成功连接到配针扫码器 {}:{}", configIp, configPort);
                return;
            } catch (java.net.ConnectException ce) {
                logger.error("连接配针扫码器失败 (尝试 {} / {}), 原因: {}",
                        retryCount+1, MAX_RETRIES, ce.getMessage());
                retryCount++;
                sleepForRetry();
            } catch (java.io.IOException e) {
                logger.error("配针扫码器连接异常", e);
                break;
            }
        }
        if (retryCount >= MAX_RETRIES) {
            logger.error("配针扫码器连接失败，已达到最大重试次数");
        }
    }

    /**
     * 关闭现有连接
     */
    private void closeExistingConnection() {
        try {
            if (currentListener != null) {
                currentListener.stopListening();
            }
            if (listenerThread != null && listenerThread.isAlive()) {
                listenerThread.interrupt();
            }
        } catch (Exception e) {
            logger.warn("关闭配针扫码器旧连接时发生异常", e);
        }
    }

    /**
     * 休眠等待重试
     */
    private void sleepForRetry() {
        try {
            Thread.sleep(RETRY_INTERVAL);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 停止PLC监控和扫码器连接
     */
    @PreDestroy
    public void stopMonitoring() {
        isMonitoring = false;
        reconnectFlag = false;

        // 停止PLC监控
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
            try {
                if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                }
            } catch (InterruptedException e) {
                scheduler.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }

        // 关闭扫码器连接
        closeExistingConnection();

        logger.info("ConfigScanner PLC监控和扫码器连接已停止");
    }

    @Override
    public void onMessageReceived(String message) {
        logger.info("onMessageReceived 被调用，message: {}", message);

        // 首先检查PLC地址507的状态，只有PLC请求扫码时才处理
        try {
            int plc507Value = modbusTcpService.readHoldingRegister(slaveId, 507);
            if (plc507Value == 0) {
                logger.error("PLC地址507为0，无需扫码，忽略消息: {}", message);
                return;  // PLC没有请求扫码，直接返回
            }
            logger.info("PLC地址507值为: {}，开始处理扫码", plc507Value);
        } catch (ModbusTransportException e) {
            logger.error("读取PLC地址507失败", e);
            return;
        }

        // 获取当前扫码的 boxId
        Integer configBoxId = redisCache.getCacheObject(GlobalVariable.CONFIG_BOX_ID);
        if (configBoxId == null) {
            logger.warn("CONFIG_BOX_ID 为 null，设置为默认值 1");
            configBoxId = 1;
            redisCache.setCacheObject(GlobalVariable.CONFIG_BOX_ID, configBoxId);
        }
        if (configBoxId < 1 || configBoxId > 16) {
            logger.error("无效的 configBoxId: {}，必须在1-16范围内", configBoxId);
            return;
        }
        logger.error("ConfigScanner 当前扫码的 boxId: {}", configBoxId);

        String scanKey = "scan:attempts:" + configBoxId;
        Map<String, String> alreadyCodes = redisCache.getCacheMap(GlobalVariable.ALREADY_CODES);

        if (message.contains("NoRead") || message.length() != 12) {
            // 扫码失败，但不再自动重试，因为PLC监控会处理重试
            logger.warn("配针扫码失败 - boxId: {}, message: {}", configBoxId, message);

            // 使用 Redis 管理扫描次数
            Integer scanAttempts = redisCache.getCacheObject(scanKey);
            if (scanAttempts == null) scanAttempts = 0;
            scanAttempts++;

            if (scanAttempts >= 10) {  // 降低重试次数，避免过度重试
                // 报警逻辑
                String alertMessage = createAlertMessage("扫码失败，请检查针盒二维码");
                webSocketHandler.sendAlert(alertMessage);
                logger.error("配针扫码失败次数过多 - boxId: {}, 已尝试{}次", configBoxId, scanAttempts);

                // 通知PLC扫码失败
                try {
                    modbusTcpService.writeHoldingRegister(slaveId, 507, 100);  // 设置为100表示扫码失败
                    logger.info("已通知PLC扫码失败 - 地址507设置为100");
                } catch (ModbusTransportException ex) {
                    logger.error("通知PLC扫码失败时写操作失败", ex);
                }

                // 重置扫描次数
                redisCache.deleteObject(scanKey);
            } else {
                // 更新重试次数
                redisCache.setCacheObject(scanKey, scanAttempts, 5, TimeUnit.MINUTES);
                logger.info("配针扫码失败，第{}次，等待PLC下次触发", scanAttempts);
            }
        } else {
            // 成功扫码逻辑
            redisCache.deleteObject(scanKey); // 清除扫描次数

            if (StringUtils.isNotEmpty(alreadyCodes.get(message))) {
                logger.info("重复处理的二维码: {}", message);
                return;
            }

            try {
                if (configBoxId > 0) {
                    // 存储二维码信息
                    Map<String, String> configBoxCodes = redisCache.getCacheMap(GlobalVariable.CONFIG_BOX_CODE);
                    if(StringUtils.isNotEmpty(configBoxCodes.get("BOXID-" + configBoxId))){
                        logger.error("二维码重复: {}", message);
                        return;
                    }
                    configBoxCodes.put("BOXID-" + configBoxId, message);
                    redisCache.setCacheMap(GlobalVariable.CONFIG_BOX_CODE, configBoxCodes);
                    logger.error("设置 CONFIG_BOX_CODE BOXID-{}", configBoxId);

                    // 更新已处理码集合
                    alreadyCodes.put(message, message);
                    redisCache.setCacheMap(GlobalVariable.ALREADY_CODES, alreadyCodes);

                    // PLC 操作
                    modbusTcpService.writeHoldingRegister(slaveId, 507, 0);
                    logger.info("成功处理二维码 - boxId: {}, code: {}", configBoxId, message);
                } else {
                    logger.error("无效的 boxId: {}", configBoxId);
                }
            } catch (ModbusTransportException ex) {
                logger.error("PLC 写操作异常", ex);
            } catch (Exception ex) {
                logger.error("处理二维码异常", ex);
            }
        }
    }

    // 辅助方法：创建报警消息
    private String createAlertMessage(String content) {
        return String.format("{\"type\":\"alert\",\"title\":\"报警信息\",\"content\":\"%s\",\"buttons\":{\"scannerConfirm\":\"确认\",\"scannerCancel\":\"取消\"}}", content);
    }
    // 旧版本的扫码处理逻辑已移除，统一使用新的onMessageReceived方法

    @Override
    public void onConnectionClosed() {
        logger.warn("配针扫码器连接被关闭，尝试重新连接...");
        if (reconnectFlag) {
            initConnection(); // 触发重连
        }
    }

    @Override
    public void onError(String errorMsg) {
        logger.error("[ERROR] " + errorMsg);
    }

    /**
     * 获取ConfigScanner状态诊断信息
     */
    public String getDiagnosticInfo() {
        try {
            int plc507Value = modbusTcpService.readHoldingRegister(slaveId, 507);
            Integer configBoxId = redisCache.getCacheObject(GlobalVariable.CONFIG_BOX_ID);
            boolean isConnected = (currentListener != null && listenerThread != null && listenerThread.isAlive());

            return String.format("ConfigScanner诊断信息: PLC地址507值=%d, 当前boxId=%s, 监控状态=%s, 扫码器连接=%s",
                    plc507Value, configBoxId, isMonitoring ? "运行中" : "已停止", isConnected ? "已连接" : "未连接");
        } catch (Exception e) {
            return "ConfigScanner诊断信息获取失败: " + e.getMessage();
        }
    }

    /**
     * 检查扫码器连接状态
     */
    public boolean isConnected() {
        return currentListener != null && listenerThread != null && listenerThread.isAlive();
    }

}
